package com.jclazz.jweb.codegen.util;

import com.jclazz.jweb.codegen.common.Constants;
import com.jclazz.jweb.codegen.common.DataMapping;
import com.jclazz.jweb.codegen.domain.*;
import com.jclazz.jweb.codegen.exceptions.CodeGeneratorException;

import java.util.*;
import java.util.stream.Collectors;

import static com.jclazz.jweb.codegen.common.Constants.*;

public class GenUtils {
    public static GenDetails prepare(GenerateSchema config, TableMeta tableMeta){

        String moduleName = config.getSubSystem();
        Helper.setupPkColumn(tableMeta);
        ClassMeta classMeta = Helper.createClassMeta(moduleName,config,tableMeta);
        Helper.setupUiScope(config,classMeta.getFields());

        GenDetails genDetails= new GenDetails();
        genDetails.setAuthor(config.getAuthor());
        genDetails.setDate(DateUtils.getDate());
        genDetails.setModuleName(moduleName);
        genDetails.setServiceId(StringUtils.replace(moduleName,".","-"));
        genDetails.setBoName(StringUtils.uncapitalize(classMeta.getClassName()));
        genDetails.setDomainType(classMeta);
        genDetails.setTableMeta(tableMeta);
        genDetails.setMenuEntry(config.getMenuEntry());

        return genDetails;
    }

    public static class Helper{
        public static ClassMeta createClassMeta(String moduleName, GenerateSchema config, TableMeta tableMeta){
            assert tableMeta.getPrimaryColumnRef() != null ;

            final String className = tableNameToClassName(tableMeta.getTableName(),true);
            ClassMeta classMeta = new ClassMeta();
            classMeta.setModulePackageName(config.getBasePackageName()+"."+moduleName + "."+className.toLowerCase());
            classMeta.setClassName(className);
            classMeta.setComment(tableMeta.getTableComment());
            classMeta.setFields(columnToVariable(tableMeta.getColumns()));

            for (VariableMeta variableMeta : classMeta.getFields()){
                if(variableMeta.getColumnBinding() == tableMeta.getPrimaryColumnRef()){
                    classMeta.setPkField(variableMeta);
                }
            }

            return classMeta;
        }

        /**
         * 逗号分隔的数据库字段名转变量名，如 user_name -> userName
         * @param columnList 字段名列表
         * @param separatorChar 分隔符
         * @return
         */
        public static Set<String> columnListToVariableNameSet(String columnList,char separatorChar){
            final Set<String> result = Arrays.asList(StringUtils.split(columnList,separatorChar))
                    .stream()
                    .map(o -> StringUtils.uncapitalize(StringUtils.convertToCamelCase(o))) // xxx_abc -> AbcDef -> abcDef
                    .collect(Collectors.toSet());
            return result;
        }
        public static void setupUiScope(GenerateSchema config, Iterable<VariableMeta> variableMetas){
            final Set<String> uiNoAdd = columnListToVariableNameSet(config.getUiAddExclude(), Constants.COMMA_CHAR);
            final Set<String> uiNoEdit = columnListToVariableNameSet(config.getUiEditExclude(), Constants.COMMA_CHAR);
            final Set<String> uiNoDetail = columnListToVariableNameSet(config.getUiDetailExclude(), Constants.COMMA_CHAR);
            final Set<String> uiNoList = columnListToVariableNameSet(config.getUiListExclude(), Constants.COMMA_CHAR);

            variableMetas.forEach(variableMeta -> {
                final Set<String> flags = new HashSet<>(Arrays.asList(UI_SCOPE_ADD,UI_SCOPE_EDIT,UI_SCOPE_DETAIL,UI_SCOPE_LIST));
                if(uiNoAdd.contains(variableMeta.getName())){
                    flags.remove(UI_SCOPE_ADD);
                }
                if(uiNoEdit.contains(variableMeta.getName())){
                    flags.remove(UI_SCOPE_EDIT);
                }

                if(uiNoDetail.contains(variableMeta.getName())){
                    flags.remove(UI_SCOPE_DETAIL);
                }
                if(uiNoList.contains(variableMeta.getName())){
                    flags.remove(UI_SCOPE_LIST);
                }
                variableMeta.setUiScope(StringUtils.join(flags.toArray()));
            });
        }

        public static ColumnMeta setupPkColumn(TableMeta tableMeta){
            List<ColumnMeta> pkColumns = tableMeta.getColumns().stream()
                    .filter(columnMeta-> MYSQL_KEY_PRI.equals(columnMeta.getColumnKey()))
                    .collect(Collectors.toList());
            if(pkColumns.isEmpty()){
                throw  CodeGeneratorException.withHint("表没有主键",tableMeta.getTableName());
            }
            if(pkColumns.size() > 1){
                final String hint = StringUtils.join(pkColumns.stream().map(o-> o.getColumnName()).collect(Collectors.toList()),',');
                throw  CodeGeneratorException.withHint("不支持联合主键",hint);
            }
            tableMeta.setPrimaryColumnRef(pkColumns.get(0));
            return pkColumns.get(0);
        }

        /**
         * 列信息转Java变量信息
         */
        private static List<VariableMeta> columnToVariable(Iterable<ColumnMeta> columns) {
            // 列信息
            List<VariableMeta> variableList = new ArrayList<>();
            columns.forEach(columnMeta -> {
                // 列名转换成Java属性名
                VariableMeta variableMeta = new VariableMeta();
                // xxx_abc -> AbcDef
                variableMeta.setNameNn(StringUtils.convertToCamelCase(columnMeta.getColumnName()));
                // xxx_abc -> AbcDef -> abcDef
                variableMeta.setName(StringUtils.uncapitalize(StringUtils.convertToCamelCase(columnMeta.getColumnName())));
                // 列的数据类型，转换成Java类型
                variableMeta.setDataType(DataMapping.javaTypeMap.get(columnMeta.getDataType()));
                variableMeta.setComment(columnMeta.getColumnComment());
                variableMeta.setColumnBinding(columnMeta);

                variableList.add(variableMeta);
                // uiScope 没有在此处进行设置
            });
            return variableList;
        }

        /**
         * 表名转换成Java类名
         */
        public static String tableNameToClassName(String tableName,boolean skipFirstUnderline){
            if (skipFirstUnderline) {
                tableName = tableName.substring(tableName.indexOf("_") + 1);
            }
            return StringUtils.convertToCamelCase(tableName);
        }
        /**
         * 将包名的最后一个单元作为模块名
         *
         * @param packageName 包名
         * @param defaultName 默认名称，如果出错就返回这个值
         * @return 模块名
         */
        public static String extractModuleName(String packageName,String defaultName) {
            final int lastIndex = packageName.lastIndexOf(".");
            final int fullLength = packageName.length();
            if(lastIndex<0 || lastIndex >= fullLength - 1){
                return defaultName;
            }
            return StringUtils.substring(packageName, lastIndex + 1, fullLength);
        }

        /**
         * 将包名的最后一个单元作为模块名
         *
         * @param packageName 包名
         * @return 模块名
         */
        public static String extractModuleName(String packageName) {
            final String moduleName = extractModuleName(packageName,null);
            if(moduleName == null){
                throw CodeGeneratorException.withHint("不是一个有效的包名称",packageName);
            }
            return moduleName;
        }

        /**
         * 根据包名称生成maven项目Java目录路径(以main目录开始)，起始和结束都没有路径分隔符,比如'org.springframework.boot' -> 'main/java/org/springframework/boot'
         * @return
         */
        public static String javaSrcPathFromPackageName(String packageName){
            return "main/java/" + StringUtils.replace(packageName,".","/");
        }
        /**
         * 根据业务模块名称生成MyBatis资源文件目录路径(以main目录开始)，起始和结束都没有路径分隔符,比如'sys.user' -> 'main/resources/mybatis/sys/user'
         * @return
         */
        public static String myBaitsResourcePathFromModuleName(String moduleName){
            return "main/resources/mybatis/" + StringUtils.replace(moduleName,".","/");
        }
        /**
         * 根据业务模块名称生成web视图文件目录路径(以main目录开始)，起始和结束都没有路径分隔符,比如'sys.user' -> 'main/resources/templates/sys/user'
         * @return
         */
        public static String webViewResourcePathFromModuleName(String moduleName){
            return "main/resources/templates/" + StringUtils.replace(moduleName,".","/");
        }
    }

}
